package com.zhuxulin.midi.event.meta;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import com.zhuxulin.midi.event.MidiEvent;
import com.zhuxulin.midi.util.MidiUtil;
import com.zhuxulin.midi.util.ReadVarLen;
/**
 * Abstract meta event</br>
 * FF type len data event
 * @author ZhuXulin
 *
 */
public abstract class MetaEvent extends MidiEvent{
	
	public static final int SEQUENCE_NUMBER		= 0;
	public static final int TEXT_EVENT			= 1;
	public static final int COPYRIGHT_NOTICE	= 2;
	public static final int TRACK_NAME			= 3;
	public static final int INSTRUMENT_NAME		= 4;
	public static final int LYRICS				= 5;
	public static final int MARKER				= 6;
	public static final int CUE_POINT			= 7;
	public static final int MIDI_CHANNEL_PREFIX	= 0x20;
	public static final int END_OF_TRACK		= 0x2F;
	public static final int TEMPO				= 0x51;
	public static final int SMPTE_OFFSET		= 0x54;
	public static final int TIME_SIGNATURE		= 0x58;
	public static final int KEY_SIGNATURE		= 0x59;
	public static final int SEQUENCER_SPECIFIC	= 0x7F;
	
	protected int mType;
	protected ReadVarLen mLength;
//	private byte[] cmd;
	protected MetaEvent(long tick, long delta, int type, ReadVarLen length) {
		super(tick, delta);
		
		mType = type & 0xFF;
		mLength = length;
		cmd=new byte[]{(byte) 0xFF,(byte) mType};
	}
	
	protected abstract int getEventSize();
	
	@Override
	public void writeToFile(OutputStream out, boolean writeType) throws IOException {
		writeToFile(out);
	}
	
	protected void writeToFile(OutputStream out) throws IOException {
		super.writeToFile(out, true);
		out.write(0xFF);
		out.write(mType);
	}

	public static MetaEvent parseMetaEvent(long tick, long delta, InputStream in) throws IOException {
		
		int type = in.read();
		
		boolean isText = false;
		switch(type) {
			case SEQUENCE_NUMBER:
			case MIDI_CHANNEL_PREFIX:
			case END_OF_TRACK:
			case TEMPO:
			case SMPTE_OFFSET:
			case TIME_SIGNATURE:
			case KEY_SIGNATURE:
				break;
			case TEXT_EVENT:
			case COPYRIGHT_NOTICE:
			case TRACK_NAME:
			case INSTRUMENT_NAME:
			case LYRICS:
			case MARKER:
			case CUE_POINT:
			case SEQUENCER_SPECIFIC:		// Not technically text, but follows same structure
			default:						// Also not technically text, but it should follow
				isText = true;
				break;
		}
		
		if(isText) {

			ReadVarLen length = new ReadVarLen(in);
			byte[] buffer = new byte[length.getValue()];
			
			in.read(buffer);
			String text = new String(buffer);
//			cmd=MidiUtil.addByte(cmd, buffer);
//			cmd=new byte[3+length.getValue()];
			switch(type) {
				case TEXT_EVENT:
					return new Text(tick, delta, text);
				case COPYRIGHT_NOTICE:
					return new CopyrightNotice(tick, delta, text);
				case TRACK_NAME:
					return new TrackName(tick, delta, text);
				case INSTRUMENT_NAME:
					return new InstrumentName(tick, delta, text);
				case LYRICS:
					return new Lyrics(tick, delta, text);
				case MARKER:
					return new Marker(tick, delta, text);
				case CUE_POINT:
					return new CuePoint(tick, delta, text);
				case SEQUENCER_SPECIFIC:
					return new SequencerSpecificEvent(tick, delta, buffer);
				default:
					return new GenericMetaEvent(tick, delta, type, length, buffer);
			}
		}
		
		switch(type) {
			case SEQUENCE_NUMBER:
				return SequenceNumber.parseSequenceNumber(tick, delta, in);
			case MIDI_CHANNEL_PREFIX:
				return MidiChannelPrefix.parseMidiChannelPrefix(tick, delta, in);
			case END_OF_TRACK:
				in.read();			// Size = 0;
				return new EndOfTrack(tick, delta);
			case TEMPO:
				return Tempo.parseTempo(tick, delta, in);
			case SMPTE_OFFSET:
				return SmpteOffset.parseSmpteOffset(tick, delta, in);
			case TIME_SIGNATURE:
				return TimeSignature.parseTimeSignature(tick, delta, in);
			case KEY_SIGNATURE:
				return KeySignature.parseKeySignature(tick, delta, in);
		}
		System.out.println("Completely broken in MetaEvent.parseMetaEvent()");
		return null;
	}
//	/**
//	 * Not implements
//	 */
//	@Override
//	public byte[] getCmd() {
//		return null;
//	}
}
